package com.hefei.garden.config.aop;

import com.alibaba.fastjson.JSON;
import com.hefei.garden.config.annotation.PreventMultipleClick;
import com.hefei.garden.config.redis.JedisUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Base64;

/**
 * @author: devil
 * @Date: 2022/10/7 20
 * @Description: 定义切面
 */
@Aspect
@Component
public class PreventMultipleClickAop {

    private static final Logger log = LoggerFactory.getLogger(PreventMultipleClickAop.class);

    private static final String CHARSET_NAME = "UTF-8";

    @Autowired
    private JedisUtils jedisUtils;

    /**
     * 切入点
     */
    @Pointcut("@annotation(com.hefei.garden.config.annotation.PreventMultipleClick)")
    public void pointcut() {
    }

    /**
     * 切入之前做啥
     *
     * @param joinPoint
     * @throws Exception
     */
    @Before("pointcut()")
    public void joinPoint(JoinPoint joinPoint) throws Exception {
        String requestStr = JSON.toJSONString(joinPoint.getArgs()[0]);
        if (StringUtils.isEmpty(requestStr) || requestStr.equalsIgnoreCase("{}")) {
            throw new RuntimeException("[防刷]入参不允许为空");
        }
        // 获取方法签名
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        // 获取方法基本信息
        Method method = joinPoint.getTarget().getClass().getMethod(methodSignature.getName(), methodSignature.getParameterTypes());
        PreventMultipleClick prevent = method.getAnnotation(PreventMultipleClick.class);
        String className = method.getDeclaringClass().getName();
        String methodName = method.getName();
        // 开始判断
        String base64Str = toBase64String(requestStr);
        String fullName = className + ":" + methodName + ":" + base64Str;
        int expire = Integer.parseInt(prevent.value());
        String resp = jedisUtils.get(fullName);
        if (StringUtils.isEmpty(resp)) {
            jedisUtils.setex(fullName, requestStr, expire);
        } else {
            String message = !StringUtils.isEmpty(prevent.message()) ? prevent.message() : expire + "秒内不允许重复请求";
            throw new RuntimeException(message);
        }
    }

    @AfterReturning(
            pointcut = "@annotation(com.hefei.garden.config.annotation.PreventMultipleClick)",
            value = "",
            returning = "str",
            argNames = "str"
    )
    public void AfterReturning(String str) {
        log.info("str: " + str);
    }

    /**
     * 对象转换为base64字符串
     *
     * @param obj
     * @return String
     * @throws Exception
     */
    private String toBase64String(String obj) throws Exception {
        if (StringUtils.isEmpty(obj)) {
            return null;
        }
        Base64.Encoder encoder = Base64.getEncoder();
        byte[] bytes = obj.getBytes(CHARSET_NAME);
        return encoder.encodeToString(bytes);
    }

    @Around("pointcut()")
    public Object aroundExecute(ProceedingJoinPoint jp) throws Throwable {
        log.info("------获取到请求参数------");
        MethodSignature methodSignature = (MethodSignature) jp.getSignature();
        Method method = methodSignature.getMethod();
        Object[] args = jp.getArgs();

        //打印入口方法以及入参
        log.info("---入口方法:{}", method.getDeclaringClass().getName());
        log.info("---请求方法:{}", method.getName());
        for (Object arg : args) {
            log.info("---请求参数:{}", JSON.toJSONString(arg));
        }

        //执行请求方法
        log.info("------开始执行请求方法:{}", method.getName());
        Long startTime = System.currentTimeMillis();
        Object o = jp.proceed();
        Long endTime = System.currentTimeMillis();

        log.info("---请求方法:{}---执行时间:{}", method.getName(), endTime - startTime);
        log.info("---返回参数:{}", JSON.toJSONString(o));
        return o;
    }

}
